/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include "nrf_assert.h"
#include "nrf_error.h"
#include "nrf_gpio.h"
#include "nrf_sdm.h"

#include "drv_audio.h"
//#include "app_debug.h"

#include "nrf_drv_pdm.h"

//#include "flash.h"
//#include "app_scheduler.h"
//#include "spi_flash.h"
//#include "nrf_drv_spi.h"
#include "nrf_delay.h"
#include "string.h"
//#include "debug.h"
#include "app_timer.h"

#include "app_util_platform.h"
#include "board_config.h"
#include "flash_data.h"
#include "flash_melcep.h"
//#include "app_task_process.h"
//#include "spi_flash.h"

//#define DBG2_EVT2 printf

//#define CONFIG_AUDIO_FRAME_SIZE_SAMPLES 128
// <o> Number of Audio Frame Buffers <2-16>
#define CONFIG_AUDIO_FRAME_BUFFERS 2

#if (PCB_BOARD == PDM_TEST_BOARD)
#define CONFIG_IO_PDM_CLK 12
#define CONFIG_IO_PDM_DATA 13
#elif (PCB_BOARD == IMX6ULL_NRF52832)
#define CONFIG_IO_PDM_CLK 28
#define CONFIG_IO_PDM_DATA 27
#elif (PCB_BOARD == IDEA_102_BOARD)
#define CONFIG_IO_PDM_CLK 21
#define CONFIG_IO_PDM_DATA 22
#endif


#if CONFIG_AUDIO_ENABLED

#define DBG_NO_FREE_FRAME_ASSERTS       0

static uint8_t m_drv_audio_start_flag = 0;
static volatile drv_audio_frame_t       m_audio_frame[CONFIG_AUDIO_FRAME_BUFFERS];
int16_t                          m_pdm_buff[2][CONFIG_AUDIO_FRAME_SIZE_SAMPLES];
int16_t                                 m_pdm_buff_sigle[CONFIG_AUDIO_FRAME_SIZE_SAMPLES/2];

static uint16_t                         m_next_frame_index = 0;
static app_sched_event_handler_t        m_audio_frame_handler = NULL;
bool                                    is_erase_over_flag = false;
bool                                    stop_pcm_data_flag = false;

static user_eraser_block_over_cb_t              user_erase_block_over_cb_call   = NULL;
static drv_audio_timer_erase_fun_t              drv_audio_timer_erase_cb        = NULL;
static drv_audio_sched_write_flash_fun_t        drv_audio_sched_write_flash_cb  = NULL;
static drv_audio_pdm_finished_fun_t             drv_audio_pdm_finished_cb       = NULL;

uint8_t write_pcm_num;

//static uint16_t                         pcmdata[16][640] __attribute__((at(0x20008000)))={0};
//int16_t                                pcmdata[100*80]      ={0};
int16_t                                pcmdata_short[CONFIG_AUDIO_FRAME_SIZE_SAMPLES/2]   ={0};

uint32_t times           = 0;                // 定义进入此函数的次数
uint32_t pretimes        = 0;                // 前一段时间不稳定，数据去掉

#if CONFIG_DEBUG_ENABLED
static uint32_t                         m_dbg_buffer_fail_cnt = 0;
#endif

 
/**
  * @brief Function for registing eraser block over event
  *
  * @param [in] event name
  * @return None
  */
void User_erase_block_over_cb_reg(user_eraser_block_over_cb_t fun)
{
    user_erase_block_over_cb_call = fun;
}

APP_TIMER_DEF(wait_erase_timer_id);

// flash擦除完成后，启动pdm
void start_pdm_process_after_flash(void)
{        
    // no stop_pcm_data_flag command
    if(!stop_pcm_data_flag)
    {
        //user_erase_block_over_cb_call();
        is_erase_over_flag = true;
        drv_audio_transmission_enable();
    }
    else
    {
        stop_pcm_data_flag = false;
    }
}


/**@brief Search for free frame index, start from value stored in m_next_frame_index
 *
 * @retval - valid number is 0..CONFIG_AUDIO_FRAME_BUFFERS-1, CONFIG_AUDIO_FRAME_BUFFERS indicates, that there is no free buffer available
 */
static uint16_t drv_audio_find_free_frame_index(void)
{
    uint16_t frame_index;
    uint16_t i;

    frame_index = m_next_frame_index;
    for (i = 0; i < CONFIG_AUDIO_FRAME_BUFFERS; i++)
    {
        //DBG2_EVT2("m_audio_frame[%d].buffer_free_flag = %d", frame_index, m_audio_frame[frame_index].buffer_free_flag);

        if (m_audio_frame[frame_index].buffer_free_flag)
        {
            return frame_index;
        }

        frame_index = (frame_index + 1) % CONFIG_AUDIO_FRAME_BUFFERS;
    }

    return CONFIG_AUDIO_FRAME_BUFFERS;
}

uint16_t buffer_count=0;

/**@brief Called from scheduler context when audio samples are ready for compression.
 *
 * @details Routine process and compresses the audio sample buffer.
 *
 * @param[in] p_event_data : Used to get index to audio buffer ready for compression.
 *
 * @param[in] event_size : Not used.
 */
static void drv_audio_buffer_handler(void * p_event_data, uint16_t event_size)
{
    drv_audio_frame_t *p_frame;
    uint16_t frame_index;
    int16_t *p_buffer;

		
    ASSERT(event_size == sizeof(p_buffer));
    p_buffer = *(void **)(p_event_data);
	
		//nrf_gpio_pin_set(20);
		//SEGGER_RTT_printf(0, "%02x\n",buffer_count++);
	
		//printf("drv_audio_buffer_handler event_size = %d\n", event_size);
    //DBG_PIO_SET(CONFIG_IO_DBG_PCM);

#if CONFIG_AUDIO_EQUALIZER_ENABLED
    drv_audio_dsp_equalizer((q15_t *)p_buffer, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
#endif /* CONFIG_AUDIO_EQUALIZER_ENABLED */

#if CONFIG_AUDIO_GAIN_CONTROL_ENABLED
    drv_audio_dsp_gain_control((q15_t *)p_buffer, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
#endif /* CONFIG_AUDIO_GAIN_CONTROL_ENABLED */
	
		//nrf_gpio_pin_clear(20);
		return;


	#if 0
    frame_index = drv_audio_find_free_frame_index();
		
		

    if (frame_index != CONFIG_AUDIO_FRAME_BUFFERS)
    {
        p_frame = (drv_audio_frame_t *)&m_audio_frame[frame_index];
        p_frame->buffer_free_flag = false;

        drv_audio_coder_encode(p_buffer, p_frame);

        // Make subsequent index preferred for next frame.
        m_next_frame_index = (frame_index + 1) % CONFIG_AUDIO_FRAME_BUFFERS;

        app_sched_event_put(&p_frame, sizeof(drv_audio_frame_t *), m_audio_frame_handler);
        DBG_PIO_CLEAR(CONFIG_IO_DBG_PCM);
    }
    else
    {
        // Invalid index means all buffers are claimed and not released.
        // Don't clear CONFIG_IO_DBG_PCM - this will make debugging pulse wider than expected and easy to spot on the logic analyzer.
        DBG0_EVT2("%s(): WARNING: Cannot allocate audio frame buffer (dbg_buffer_fail_cnt = %d)!", __func__, ++m_dbg_buffer_fail_cnt);
#if (DBG_FREE_FRAME_ASSERTS)
        APP_ERROR_CHECK_BOOL(false);
#endif
    }
		
		#endif
}

uint32_t drv_audio_transmission_enable(void)
{
    unsigned int i;

    //drv_audio_coder_init();

    m_next_frame_index = 0;
    for (i = 0; i < CONFIG_AUDIO_FRAME_BUFFERS; i++ )
    {
        m_audio_frame[i].buffer_free_flag = true;
    }

    return nrf_drv_pdm_start();
}

uint32_t drv_audio_transmission_disable(void)
{
    return nrf_drv_pdm_stop();
}
   int16_t * pdm_flashwrite_buff_a;

static void write_flash_pcm_data_sch(void * p_event_data, uint16_t event_size)
{
    //uint8_t buf[16]  ADDR_ALIGNED_4 = {0} ;
//////    flash_write(FLASH_BLOCK18_PCMDATA,buf,16,0);
    
    //drv_audio_transmission_enable();
    ;

}

void m_flash_storage_event_handler()
{
    //SPI_FLASH_PageWrite((uint8_t *)pcmdata_short, times , 160, 0 );
}

void pcmdata_process_event_handler()
{
    ;
}

void pcmdata_read_handler()
{
    nrf_delay_ms(10);
    for(uint8_t i=0;i<100;i++)
    {
        ////SPI_FLASH_PageRead((uint8_t *)(spim_rxbuf), i, 160, 0);
//        memcpy(pcmdata + 80 * i,spim_rxbuf+4,160);
        nrf_delay_ms(10);
    }
    __NOP;
}


static int16_t max_abs_data(int16_t *buff,uint32_t len)
{
    uint32_t i;
    int16_t max_data = 0;

    for(i = 0; i < len; i++)
    {
        if(max_data < abs(buff[i]))
        {
            max_data = abs(buff[i]);
        }
    }
    return max_data;
}

static void drv_audio_pdm_event_handler(uint32_t *p_buffer, uint16_t length)
{
    pdm_flashwrite_buff_a           = m_pdm_buff[0];
    int16_t * pdm_flashwrite_buff_b = m_pdm_buff[1];
    //static uint32_t* audioadd=(uint32_t*)(0x00005000);
    ASSERT(length == CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
    
    // no stop_pcm_data_flag command
    if(!stop_pcm_data_flag)
    { 
#if CONFIG_DEBUG_ENABLED
        if(pretimes < 15)
        {
            pretimes++;
            return;
        }
        if(1 == m_drv_audio_start_flag)
        {
            if ((int16_t *)p_buffer == m_pdm_buff[0])
            {
                if(max_abs_data(m_pdm_buff[0],CONFIG_AUDIO_FRAME_SIZE_SAMPLES) < 1000)
                {
                    return;
                }
                else
                {
                    m_drv_audio_start_flag = 0;
                }
            }
            else if ((int16_t *)p_buffer == m_pdm_buff[1])
            {
                if(max_abs_data(m_pdm_buff[1],CONFIG_AUDIO_FRAME_SIZE_SAMPLES) < 1000)
                {
                    return;
                }
                else
                {
                    m_drv_audio_start_flag = 0;
                }
            }
        }
        if ((int16_t *)p_buffer == m_pdm_buff[0])
        {
            //DBG_PIO_PULSE(CONFIG_IO_DBG_HALF); 
            if(times < CONFIG_AUDIO_FRAME_TIMES)
            {
                for(uint16_t i=0;i<length/2;i++)
                {
//                    pcmdata[80*times+i] = m_pdm_buff[0][i*2];
                    pcmdata_short[i] = m_pdm_buff[0][i*2];
                }
                times++;
                drv_audio_sched_write_flash_cb(pcmdata_short,times, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
                //app_sched_event_put(NULL, 0, m_flash_storage_event_handler);
            }
            else
            {
                drv_audio_transmission_disable();
                times       = 0;
                pretimes    = 0;
                drv_audio_pdm_finished_cb();
//                app_sched_event_put(NULL,0,pcmdata_read_handler);
                //app_sched_event_put(NULL, 0, pcmdata_process_event_handler);
            }
           
        }
        else if ((int16_t *)p_buffer == m_pdm_buff[1])
        {         
            //DBG_PIO_PULSE(CONFIG_IO_DBG_FULL);
            if(times < CONFIG_AUDIO_FRAME_TIMES)
            {
                for(uint16_t i=0;i<length/2;i++)
                {
//                    pcmdata[80*times+i] = m_pdm_buff[1][i*2];
                    pcmdata_short[i] = m_pdm_buff[1][i*2];
                }
                times++;
                drv_audio_sched_write_flash_cb(pcmdata_short,times, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
                //app_sched_event_put(NULL, 0, m_flash_storage_event_handler);
            }else
            {
                drv_audio_transmission_disable();
                times       = 0;
                pretimes    = 0;
                drv_audio_pdm_finished_cb();
//                app_sched_event_put(NULL,0,pcmdata_read_handler);
                //app_sched_event_put(NULL, 0, pcmdata_process_event_handler);
            }
        }   
        else
        {
            APP_ERROR_CHECK_BOOL(false);  // assert error for unsupported events
        }
#endif /* CONFIG_DEBUG_ENABLED */
			
    //SEGGER_RTT_printf(0,"input: %02X %02X .. %02X %02X %d -> current\n", p_buffer[0], p_buffer[1], p_buffer[length-2], p_buffer[length-1], length);
		//SEGGER_RTT_printf(0,"%02d: %04X %04X .. %04X %04X\n", buffer_count++, (uint16_t)p_buffer[0], (uint16_t)p_buffer[1], (uint16_t)p_buffer[length-2], (uint16_t)p_buffer[length-1]);
    
		//SEGGER_RTT_printf(0, "count:");
//		for (int i=0; i < length; i++)
//		{
//			SEGGER_RTT_printf(0, "%04X ", (uint16_t)p_buffer[i]);
//		}
//		SEGGER_RTT_printf(0, "\n");
			
		//APP_ERROR_CHECK(app_sched_event_put(&p_buffer, sizeof(p_buffer), drv_audio_buffer_handler));
    }
    else
    {
        stop_pcm_data_flag = false;
    }
}

/*******************************************************************************
* Function Name  : start_pcm_data
* Description    : Start pcm data process and save the pcmdata to spi_flash. It 
*                  is used for external fun to call it
* Input          : None
* Output         : None
* Return         : err
*******************************************************************************/
uint8_t start_pcm_data(uint8_t num)
{
    uint8_t err_code = 0;
    stop_pcm_data_flag  = false;
    // 用于存储pdm数据
    flash_data_clear(0,FLASH_DATA_BLOCK_NUM);
    m_drv_audio_start_flag = 1;
    //drv_audio_timer_erase_cb();
    return err_code; 
}

/**
*@brief Stop pcm data process 
*
*@param [in]    None
*@param [out]   None
*@return        None
*/
void stop_pcm_data(void)
{
    stop_pcm_data_flag  = true;
    drv_audio_transmission_disable();                 
    times       = 0;
    pretimes    = 0;
}

bool drv_audio_timer_erase_reg(drv_audio_timer_erase_fun_t fun)
{
    drv_audio_timer_erase_cb = fun;
    return true;
}

bool drv_audio_sched_callback_reg(drv_audio_sched_write_flash_fun_t fun)
{
    drv_audio_sched_write_flash_cb = fun;
    return true;
}

bool drv_audio_pdm_finished_reg(drv_audio_pdm_finished_fun_t fun)
{
    drv_audio_pdm_finished_cb = fun;
    return true;
}


uint32_t drv_audio_init(app_sched_event_handler_t audio_frame_handler)
{
    nrf_drv_pdm_config_t pdm_cfg = NRF_DRV_PDM_DEFAULT_CONFIG(CONFIG_IO_PDM_CLK,
                                                              CONFIG_IO_PDM_DATA,
                                                              m_pdm_buff[0],
                                                              m_pdm_buff[1],
                                                              CONFIG_AUDIO_FRAME_SIZE_SAMPLES);

    if ((audio_frame_handler == NULL))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    m_audio_frame_handler   = audio_frame_handler;
    pdm_cfg.gain_l          = CONFIG_AUDIO_PDM_GAIN;
    pdm_cfg.gain_r          = CONFIG_AUDIO_PDM_GAIN;

		
		
		
		//nrf_gpio_cfg_output(21);
		
		
    return nrf_drv_pdm_init(&pdm_cfg, drv_audio_pdm_event_handler);
}

#endif /* CONFIG_AUDIO_ENABLED */
